import math

class Webcoord:
    def wgs84_to_tileXY(self, coordinate, zoom):
        """
        计算WGS84经纬度坐标所属的切片索引(xyz切片方案)
        @params coordinate<list>: [wgs84_longitude, wgs84_latitude]
        @params zoom<int>: 1~18 整形数值
        @return: tile_tree_index<list>: [tileX, tileY, level]
        """
        lontitude = coordinate[0]
        latitude = coordinate[1]
        tileX = math.floor((lontitude + 180) / 360 * (2 ** zoom))

        parma1 = math.tan(math.radians(latitude))
        parma2 = 1 / (math.cos(math.radians(latitude)))
        parma3 = math.log(parma1 + parma2, math.e)
        parma4 = parma3 / math.pi
        tileY = math.floor((1 - parma4) / 2 * (2 ** zoom))
        return [tileX, tileY, zoom]

    def pixelXY_in_tileXYZ_to_wgs84(self, tileXY_list, pixelXY_list):
        """
        根据已知切片的像素坐标，计算经纬度
        @pamars tileXY_list<list>: [tileX, tileY, level]
        @params pixelXY_list<list>: [pixX, pixY]
        @return wgs84 coordinate <list>: [wgs84_lontitude, wgs84_latitude]
        """
        tileX = tileXY_list[0]
        tileY = tileXY_list[1]
        zoom = tileXY_list[2]
        pixX = pixelXY_list[0]
        pixY = pixelXY_list[1]
        lon = (tileX + pixX / 256) / (2 ** zoom) * 360 - 180

        parma1 = (tileY + pixY / 256) / (2 ** zoom)
        parma2 = math.sinh(math.pi - 2 * math.pi * parma1)
        parma3 = math.atan(parma2)
        lat = math.degrees(parma3)
        return [lon, lat]

    def wgs84_to_web_mercator(self, coordList):
        """
        经纬度（[longitude,latitude]）转web墨卡托平面坐标([x,y])
        @params list: [longitude, latitude]
        @return list: [x, y]
        """
        semi_perimeter = 2 * math.pi * 6378137 / 2
        lon = coordList[0]
        lat = coordList[1]
        x = lon * semi_perimeter / 180
        _y = math.log(math.tan((90 + lat) * math.pi / 360)) / (math.pi / 180)
        y = _y * semi_perimeter / 180
        return [round(x, 4), round(y, 4)]

    def web_mercator_to_wgs84(self, coordList):
        """
        web墨卡托平面坐标([x, y])转 经纬度（[longitude,latitude]）
        @params list: [x, y]
        @return list: [longitude, latitude]
        """
        semi_perimeter = 2 * math.pi * 6378137 / 2
        x = coordList[0]
        y = coordList[1]
        lon = x / semi_perimeter * 180
        _lat = y / semi_perimeter * 180
        lat = (
            180
            / math.pi
            * (2 * math.atan(math.exp(_lat * math.pi / 180)) - math.pi / 2)
        )
        return [round(lon, 8), round(lat, 8)]

    def wgs84_to_gcj02(self, coordinate):
        """
        WGS经纬度坐标转GCJ02 加偏
        @params wgs84_coordinate <list>: [wgs84_longititude, wgs84_latitude]
        @return gcj02_coordinate <list>: [gcj02_longitude, gcj02_latitude]
        """
        a = 6378245.0
        ee = 0.00669342162296594323
        wgs84_longitude = coordinate[0]
        wgs84_latitude = coordinate[1]
        dLat = self.__transform_lat(wgs84_longitude - 105.0, wgs84_latitude - 35.0)
        dLon = self.__transform_lon(wgs84_longitude - 105.0, wgs84_latitude - 35.0)
        radLat = wgs84_latitude / 180.0 * math.pi
        magic = math.sin(radLat)
        magic = 1 - ee * magic * magic
        sqrtMagic = math.sqrt(magic)
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * math.pi)
        dLon = (dLon * 180.0) / (a / sqrtMagic * math.cos(radLat) * math.pi)
        mgLat = wgs84_latitude + dLat
        mgLon = wgs84_longitude + dLon
        return [mgLon, mgLat]

    def gcj02_to_wgs84(self, coordinate):
        """
        GCJ02坐标转WGS84 纠偏
        @params gcj02_coordinate <list>: [gcj02_longitude, gcj02_latitude]
        @return wgs84_coordinate <list>: [wgs84_longititude, wgs84_latitude]
        浮点计算精度和拟合算法问题，有1米左右的转换误差
        """
        a = 6378245.0
        ee = 0.00669342162296594323
        gcj02_longitude = coordinate[0]
        gcj02_latitude = coordinate[1]
        dlat = self.__transform_lat(gcj02_longitude - 105.0, gcj02_latitude - 35.0)
        dlng = self.__transform_lon(gcj02_longitude - 105.0, gcj02_latitude - 35.0)
        radlat = gcj02_latitude / 180.0 * math.pi
        magic = math.sin(radlat)
        magic = 1 - ee * magic * magic
        sqrtmagic = math.sqrt(magic)
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * math.pi)
        dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * math.pi)
        mglat = gcj02_latitude + dlat
        mglon = gcj02_longitude + dlng
        return [gcj02_longitude * 2 - mglon, gcj02_latitude * 2 - mglat]

    def __transform_lat(self, x, y):
        result = (
            -100.0
            + 2.0 * x
            + 3.0 * y
            + 0.2 * y * y
            + 0.1 * x * y
            + 0.2 * math.sqrt(abs(x))
        )
        result += (
            (20.0 * math.sin(6.0 * x * math.pi) + 20.0 * math.sin(2.0 * x * math.pi))
            * 2.0
            / 3.0
        )
        result += (
            (20.0 * math.sin(y * math.pi) + 40.0 * math.sin(y / 3.0 * math.pi))
            * 2.0
            / 3.0
        )
        result += (
            (160.0 * math.sin(y / 12.0 * math.pi) + 320 * math.sin(y * math.pi / 30.0))
            * 2.0
            / 3.0
        )
        return result

    def __transform_lon(self, x, y):
        result = (
            300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * math.sqrt(abs(x))
        )
        result += (
            (20.0 * math.sin(6.0 * x * math.pi) + 20.0 * math.sin(2.0 * x * math.pi))
            * 2.0
            / 3.0
        )
        result += (
            (20.0 * math.sin(x * math.pi) + 40.0 * math.sin(x / 3.0 * math.pi))
            * 2.0
            / 3.0
        )
        result += (
            (
                150.0 * math.sin(x / 12.0 * math.pi)
                + 300.0 * math.sin(x / 30.0 * math.pi)
            )
            * 2.0
            / 3.0
        )
        return result

    def bd09_to_gcj02(self, coordinate):
        """
        bd09坐标系转向GCJ02
        @params coordinate<list> : [bd09_longitude,bd09_latitude]
        @return coordinate<list> : [gcj02_longitude,gcj02_latitude]
        """
        bd09_longitude = coordinate[0]
        bd09_latitude = coordinate[-1]
        x = bd09_longitude - 0.0065
        y = bd09_latitude - 0.006
        z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * math.pi * 3000.0 / 180.0)
        theta = math.atan2(y, x) - 0.000003 * math.cos(x * math.pi * 3000.0 / 180.0)
        gcj02_longitude = z * math.cos(theta)
        gcj02_latitude = z * math.sin(theta)
        return [gcj02_longitude, gcj02_latitude]

    def bd09_to_wgs84(self, bd09_coordinate):
        """
        bd09坐标系统转向wgs84
        @params bd09_coordinate <list> : [bd09_longitude, bd09_latitude]
        @return wgs84_coordinate <list> : [wgs84_longitude, wgs84_latitude]
        """
        gcj02_coordinate = self.bd09_to_gcj02(bd09_coordinate)
        return self.gcj02_to_wgs84(gcj02_coordinate)
